package qfmysql

import (
	"database/sql"
	"errors"
	"fmt"
	"os"

	"github.com/gin-gonic/gin"

	common "example.com/m/com.common"
	qfcfg "example.com/m/com.qfcfg"
	qflog "example.com/m/com.qflog"
	models "example.com/m/com.xorm_models/models"
	_ "github.com/go-sql-driver/mysql"
	"github.com/go-xorm/builder"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
)

type ORMEngine struct {
	Engine *xorm.Engine
}

// MysqlHandler export var
var MysqlHandler *ORMEngine

// NewEngine def
func NewEngine() (*ORMEngine, error) {
	//连接数据库
	dbInfo := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8",
		qfcfg.ServerConfig.Mysql.Username, qfcfg.ServerConfig.Mysql.Password.Value,
		qfcfg.ServerConfig.Mysql.Host, qfcfg.ServerConfig.Mysql.Port, qfcfg.ServerConfig.Mysql.Dbname)
	engine, err := xorm.NewEngine("mysql", dbInfo)

	if err != nil {
		qflog.Sugare.Errorf("connect mysql failed, err: %v", err)
		return nil, err
	}

	//连接测试
	if err := engine.Ping(); err != nil {
		qflog.Sugare.Errorf("ping mysql failed, err: %v", err)
		return nil, err
	}

	engine.SetMaxOpenConns(8)
	engine.SetMaxIdleConns(4)
	if qfcfg.ServerConfig.Mysql.Debug {
		f, err := os.Create("./log/mysql.log")
		if err != nil {
			qflog.Sugare.Errorf("Create sql.log failed, err: %v", err)
			return nil, err
		}
		var logger *xorm.SimpleLogger = xorm.NewSimpleLogger(f)
		engine.SetLogger(logger)
		engine.Logger().SetLevel(core.LOG_DEBUG)
		engine.ShowSQL(true)
		engine.ShowExecTime(true)
	}

	ORMEngine := ORMEngine{
		Engine: engine,
	}

	return &ORMEngine, nil
	// qflog.Sugare.Infof("connect mysql succeed.")
	// defer x.Close() //延迟关闭数据库
}

// InitMysqlDB def
func InitMysqlDB() error {
	var err error
	MysqlHandler, err = NewEngine()
	if err != nil {
		return err
	}
	return nil
}

// FindSQL def
func FindSQL(result interface{}, query *builder.Builder) (interface{}, error) {
	cmd, args, err := query.ToSQL()

	if err != nil {
		qflog.Sugare.Errorf("sql:%s err:%v", cmd, err)
		return nil, err
	}
	err = MysqlHandler.Engine.SQL(cmd, args...).Find(result)
	if err != nil {
		qflog.Sugare.Errorf("sql:%s err:%v", cmd, err)
		return nil, err
	}

	return result, nil
}

// ExecSQL def
func ExecSQL(ctx *gin.Context, query *builder.Builder) (sql.Result, error) {
	traceID := ctx.Request.Header.Get(common.TraceID)
	cmd, args, err := query.ToSQL()

	if err != nil {
		qflog.Sugare.Errorf("sql:%s err:%v", cmd, err)
		return nil, err
	}

	res, err := MysqlHandler.Engine.Exec(cmd, args...)
	if err != nil {
		qflog.Sugare.Errorf("sql:%s err:%v", cmd, err)
		return nil, err
	}

	rowsAffected, _ := res.RowsAffected()
	lastInsertId, _ := res.LastInsertId()

	qflog.Sugare.Infof("TraceID=%s|cmd:%s args:%v, rowsAffected:%d, lastInsertId:%d", traceID, cmd, args, rowsAffected, lastInsertId)
	return res, nil
}

// InsertLogInfo def
func InsertLogInfo(ctx *gin.Context, logInfo models.TLogInfo) (int64, error) {
	query := builder.MySQL().Insert(
		builder.Eq{"v_session_id": logInfo.VSessionId}, builder.Eq{"v_major_type": logInfo.VMajorType},
		builder.Eq{"v_minor_type": logInfo.VMinorType}, builder.Eq{"v_opt_user": logInfo.VOptUser},
		builder.Eq{"v_client_ip": logInfo.VClientIp}, builder.Eq{"v_opt_time": logInfo.VOptTime},
		builder.Eq{"v_opt_info": logInfo.VOptInfo}).
		Into("t_log_info")

	res, err := ExecSQL(ctx, query)
	if err != nil {
		return 0, err
	}

	if err != nil {
		return 0, err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}
	return id, nil
}

// QueryUserInfo def
func QueryUserInfo(ctx *gin.Context, userName string) (*models.TUser, error) {
	query := builder.MySQL().Select("*").From("t_user").
		Where(builder.Eq{"u_name": userName})

	var result []models.TUser
	_, err := FindSQL(&result, query)
	if err != nil {
		return nil, err
	}

	if len(result) == 0 {
		return nil, ErrNoRecord
	}

	return &result[0], nil
}

// InsertUserInfo def
func InsertUserInfo(ctx *gin.Context, userInfo models.TUser) (int64, error) {
	query := builder.MySQL().Insert(
		builder.Eq{"u_name": userInfo.UName}, builder.Eq{"u_pwd": userInfo.UPwd},
		builder.Eq{"u_level": userInfo.ULevel}, builder.Eq{"u_error_cnt": userInfo.UErrorCnt},
		builder.Eq{"u_locked_time": userInfo.ULockedTime}).
		Into("t_user")

	res, err := ExecSQL(ctx, query)
	if err != nil {
		return 0, err
	}

	if err != nil {
		return 0, err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}
	return id, nil
}

// UpdateUserInfo def
func UpdateUserInfo(ctx *gin.Context, userInfo models.TUser) (int64, error) {
	query := builder.MySQL().Update(
		builder.Eq{"u_error_cnt": userInfo.UErrorCnt}, builder.Eq{"u_locked_time": userInfo.ULockedTime}).
		Where(builder.Eq{"u_name": userInfo.UName}).From("t_user")

	res, err := ExecSQL(ctx, query)
	if err != nil {
		return 0, err
	}

	if err != nil {
		return 0, err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}
	return id, nil
}

var (
	ErrNoRecord = errors.New("no mysql record")
	MultiRecord = errors.New("multi mysql record")
)
